package redis

import (
	"errors"
	"gitee.com/Cauchy_AQ/gokv"
	"math"
	"time"
)

var (
	ErrWrongTypeOperation = errors.New("wrong type operation")
	ErrWrongTypeValue     = errors.New("wrong type value")
	ErrExpireKey          = errors.New("expire value")
)

const (
	// initialListMark List 数据结构的头尾指针标记值
	initialListMark = math.MaxUint64 / 2
)

// redisDataType 自定义 redis 类型
type redisDataType = byte

const (
	String redisDataType = iota
	Hash
	Set
	List
	ZSet
)

// RedisDataStruct redis数据结构
type RedisDataStruct struct {
	db *gokv.DB
}

// NewRedisDataStruct 根据 gokv.Options 创建新的 Redis 数据结构
func NewRedisDataStruct(options *gokv.Options) (*RedisDataStruct, error) {
	db, err := gokv.Open(options)
	if err != nil {
		return nil, err
	}
	return &RedisDataStruct{db: db}, nil
}

// InitialRedisDataStruct 根据 gokv.DB 初始化 Redis 数据结构
func InitialRedisDataStruct(db *gokv.DB) *RedisDataStruct {
	if db == nil {
		return nil
	}
	return &RedisDataStruct{db: db}
}

// Close 关闭 redis 结构
func (r *RedisDataStruct) Close() error {
	return r.db.Close()
}

// findAndCreateMetadata 根据 key 查询对应类型的元数据信息，不存在则创建新 metadata
func (r *RedisDataStruct) findAndCreateMetadata(key []byte, dataType redisDataType) (*metadata, error) {
	meta, exist, err := r.findMetadata(key, dataType)
	if err != nil {
		return nil, err
	}

	if !exist {
		// 不存在，则创建
		meta = &metadata{
			dataType: dataType,
			expire:   0,
			version:  time.Now().UnixNano(),
			size:     0,
		}

		if meta.dataType == List {
			meta.head = initialListMark
			meta.tail = initialListMark
		}
	}

	return meta, nil
}

// findMetadata 根据 key 查询对应类型的元数据信息，返回元数据信息及数据是否存在
func (r *RedisDataStruct) findMetadata(key []byte, dataType redisDataType) (*metadata, bool, error) {
	val, err := r.db.Get(key)
	if err != nil && err != gokv.ErrKeyNotFound {
		return nil, false, err
	}

	var meta *metadata
	var exist bool = true
	if err == gokv.ErrKeyNotFound {
		// 元数据信息不存在
		exist = false
	} else {
		// 元数据信息存在
		meta = decodeMetadata(val)

		// 元数据信息过期
		if meta.expire != 0 && meta.expire <= time.Now().Unix() {
			exist = false
		}

		// 类型操作不匹配
		if meta.dataType != dataType {
			return nil, exist, ErrWrongTypeOperation
		}
	}

	return meta, exist, nil
}
